package ulog

import (
	"fmt"
	baselog "log"
	"os"
	"strings"
	"sync"
)

type LogLevel uint8

const (
	DebugLevel LogLevel = iota
	InfoLevel
	WarnLevel
	ErrorLevel
	FatalLevel
	PanicLevel
)

type logger struct {
	sync.Mutex
	level                              LogLevel
	dins, iins, wins, eins, fins, pins *baselog.Logger
}

var (
	ulog = &logger{
		dins:  baselog.New(os.Stderr, "[D] ", baselog.LstdFlags|baselog.Lshortfile),
		iins:  baselog.New(os.Stderr, "[I] ", baselog.LstdFlags|baselog.Lshortfile),
		wins:  baselog.New(os.Stderr, "[W] ", baselog.LstdFlags|baselog.Lshortfile),
		eins:  baselog.New(os.Stderr, "[E] ", baselog.LstdFlags|baselog.Lshortfile),
		fins:  baselog.New(os.Stderr, "[F] ", baselog.LstdFlags|baselog.Lshortfile),
		pins:  baselog.New(os.Stderr, "[P] ", baselog.LstdFlags),
		level: InfoLevel,
	}
)

type LogLevelString string

func (lls LogLevelString) GetLevel() LogLevel {
	switch strings.ToLower(string(lls)) {
	case "d", "debug":
		return DebugLevel
	case "i", "info":
		return InfoLevel
	case "w", "warn", "warning":
		return WarnLevel
	case "e", "err", "error":
		return ErrorLevel
	case "f", "fatal":
		return FatalLevel
	case "p", "panic":
		return PanicLevel
	default:
		return InfoLevel
	}
}

func GetLogLevel() LogLevel {
	ulog.Lock()
	defer ulog.Unlock()

	return ulog.level
}

func SetLogLevel(level LogLevel) {
	ulog.Lock()
	defer ulog.Unlock()
	if level <= 5 {
		ulog.level = level
		return
	}

	ulog.level = WarnLevel
}

func (l *logger) SetLogLevel(level LogLevel) {
	l.Lock()
	defer l.Unlock()
	if level <= 5 {
		l.level = level
		return
	}

	l.level = WarnLevel
}

func Debugf(msg string, args ...interface{}) {
	if ulog.level <= DebugLevel {
		ulog.dins.Output(2, fmt.Sprintf(msg, args...))
	}
}

func Infof(msg string, args ...interface{}) {
	if ulog.level <= InfoLevel {
		ulog.iins.Output(2, fmt.Sprintf(msg, args...))
	}
}
func Warnf(msg string, args ...interface{}) {
	if ulog.level <= WarnLevel {
		ulog.wins.Output(2, fmt.Sprintf(msg, args...))
	}
}

func Errorf(msg string, args ...interface{}) {
	if ulog.level <= ErrorLevel {
		ulog.eins.Output(2, fmt.Sprintf(msg, args...))
	}
}
func Fatalf(msg string, args ...interface{}) {
	if ulog.level <= FatalLevel {
		ulog.fins.Output(2, fmt.Sprintf(msg, args...))
		os.Exit(1)
	}
}
func Panicf(msg string, args ...interface{}) {
	if ulog.level <= PanicLevel {
		ulog.fins.Panicf(msg, args...)
	}
}

func Debug(args ...interface{}) {
	if ulog.level <= DebugLevel {
		ulog.dins.Output(2, fmt.Sprint(args...))
	}
}

func Info(args ...interface{}) {
	if ulog.level <= InfoLevel {
		ulog.iins.Output(2, fmt.Sprint(args...))
	}
}
func Warn(args ...interface{}) {
	if ulog.level <= WarnLevel {
		ulog.wins.Output(2, fmt.Sprint(args...))
	}
}

func Error(args ...interface{}) {
	if ulog.level <= ErrorLevel {
		ulog.eins.Output(2, fmt.Sprint(args...))
	}
}
func Fatal(args ...interface{}) {
	if ulog.level <= FatalLevel {
		ulog.fins.Output(2, fmt.Sprint(args...))
		os.Exit(1)
	}
}
func Panic(args ...interface{}) {
	if ulog.level <= PanicLevel {
		ulog.fins.Panic(args...)
	}
}

func (l *logger) Debugf(msg string, args ...interface{}) {
	if l.level <= DebugLevel {
		l.dins.Output(2, fmt.Sprintf(msg, args...))
	}
}

func (l *logger) Infof(msg string, args ...interface{}) {
	if l.level <= InfoLevel {
		l.iins.Output(2, fmt.Sprintf(msg, args...))
	}
}
func (l *logger) Warnf(msg string, args ...interface{}) {
	if l.level <= WarnLevel {
		l.wins.Output(2, fmt.Sprintf(msg, args...))
	}
}

func (l *logger) Errorf(msg string, args ...interface{}) {
	if l.level <= ErrorLevel {
		l.eins.Output(2, fmt.Sprintf(msg, args...))
	}
}
func (l *logger) Fatalf(msg string, args ...interface{}) {
	if l.level <= FatalLevel {
		l.fins.Output(2, fmt.Sprintf(msg, args...))
		os.Exit(1)
	}
}
func (l *logger) Panicf(msg string, args ...interface{}) {
	if l.level <= PanicLevel {
		l.fins.Panicf(msg, args...)
	}
}

func (l *logger) Debug(args ...interface{}) {
	if l.level <= DebugLevel {
		l.dins.Output(2, fmt.Sprint(args...))
	}
}

func (l *logger) Info(args ...interface{}) {
	if l.level <= InfoLevel {
		l.iins.Output(2, fmt.Sprint(args...))
	}
}
func (l *logger) Warn(args ...interface{}) {
	if l.level <= WarnLevel {
		l.wins.Output(2, fmt.Sprint(args...))
	}
}

func (l *logger) Error(args ...interface{}) {
	if l.level <= ErrorLevel {
		l.eins.Output(2, fmt.Sprint(args...))
	}
}
func (l *logger) Fatal(args ...interface{}) {
	if l.level <= FatalLevel {
		l.fins.Output(2, fmt.Sprint(args...))
		os.Exit(1)
	}
}
func (l *logger) Panic(args ...interface{}) {
	if l.level <= PanicLevel {
		l.fins.Panic(args...)
	}
}
